/**
 * 技能系统串联测试脚本（基于 4437 模板）
 * 覆盖：1382 查看角色技能 → 1409 可学副将 → 1396 学习/重修 → 1392 升级 → 2020/2021/2022 技能书加熟练度
 */

const http = require('http');
const WebSocket = require('ws');

const CONFIG = {
    server: {
        host: process.env.SVC_HOST || 'localhost',
        port: Number(process.env.SVC_PORT || 3000),
        protocol: process.env.SVC_PROTO || 'http',
    },
    testUser: {
        mobile: process.env.TEST_MOBILE || '17091279999',
        passwordHash: process.env.TEST_PWD_HASH || 'e10adc3949ba59abbe56e057f20f883e',
    },
    testCharacter: { name: '技能流测', sex: 1, roleType: 1, headIndex: 2 },
    // 用于2020/2021/2022：尽量从返回动态取，这里仅兜底
    bookGidFallbacks: [131, 133],
};

class SkillFlowTester {
    constructor() {
        this.token = null;
        this.userId = null;
        this.ws = null;
        this.characters = [];
        this.selectedCharacterId = null;
    }

    async run() {
        console.log('🏯 技能系统串联测试开始...');
        await this.waitServer();
        await this.ensureUser();
        await this.httpLogin();
        await this.openWS();
        await this.delay(800);
        await this.queryRoles();
        if (!this.characters.length) {
            await this.createRole();
            await this.delay(500);
            await this.queryRoles();
        }
        await this.selectFirstRole();

        // 1382：查看角色技能
        await this.sendAndExpect(1382, {}, 0);

        // 1409：查询可学习技能的副将；若没有，则尝试生成副将后重查
        let list1409 = await this.sendAndExpect(1409, { pageNum: 0 }, 0);
        const deputy = (list1409.data || [])[0];
        if (deputy) {
            // 1396 action=1 学习（随机）
            await this.sendAndExpect(1396, { cid: deputy.id, action: 1, skillid: 0 }, 0);
            // 1396 action=2 重修（随机，扣2万银）
            try {
                await this.sendAndExpect(1396, { cid: deputy.id, action: 2, skillid: 0 }, 0);
            } catch (e) {
                console.log('⚠️ 重修失败(可能银两不足)：', e.message);
            }
        } else {
            console.log('ℹ️ 当前无可学习技能的副将，跳过 1396 分支');
        }

        // 2020：查询可用技能书的目标（含自身-9 与副将）
        const gid = CONFIG.bookGidFallbacks[0];
        const res2020 = await this.sendAndExpect(2020, { gid, pageNum: 0 }, 0);
        const targetList = res2020.list || res2020.data || [];
        const selfTarget = targetList.find((x) => x.id === -9) || targetList[0];
        if (!selfTarget) throw new Error('没有可用技能书目标');

        // 2021：查询目标可用技能
        const gidForQuery = CONFIG.bookGidFallbacks[1] || gid;
        const res2021 = await this.sendAndExpect(2021, { gid: gidForQuery, cid: selfTarget.id, pageNum: 0 }, 0);
        const skillOpt = (res2021.list || [])[0];
        if (skillOpt) {
            // 2022：给该技能加熟练度 1 本（背包不足则跳过）
            try {
                await this.sendAndExpect(2022, { gid: gidForQuery, cid: selfTarget.id, sid: skillOpt.id, c: 1 }, 0);
            } catch (e) {
                console.log('⚠️ 2022 失败(可能无技能书)：', e.message);
            }
        }

        // 1392：尝试升级角色技能（选用上一步的技能或兜底1）
        const skillId = (skillOpt && skillOpt.id) || 1;
        const res1392 = await this.send(1392, { id: skillId });
        console.log('🔁 1392 返回:', JSON.stringify(res1392));

        await this.closeWS();
        console.log('✅ 测试完成');
    }

    // 基础工具
    async httpLogin() {
        const login = await this.httpReq('/user-service/user/login', 'POST', {
            mobile: CONFIG.testUser.mobile,
            pwd: CONFIG.testUser.passwordHash,
        });
        if (login.code !== 0) throw new Error(login.msg || '登录失败');
        this.userId = login.data.userId;
        this.token = login.data.accessToken;
    }

    async ensureUser() {
        try {
            await this.httpReq('/user-service/user/register', 'POST', {
                mobile: CONFIG.testUser.mobile,
                pwd: CONFIG.testUser.passwordHash,
                captcha: '123456',
            });
            console.log('📝 注册成功');
        } catch (e) {
            console.log('👤 注册跳过(可能已存在):', e.message);
        }
    }

    async openWS() {
        const url = `ws://${CONFIG.server.host}:${CONFIG.server.port}/user-service/websocket?t=${this.token}`;
        this.ws = new WebSocket(url);
        await new Promise((resolve, reject) => {
            const timer = setTimeout(() => reject(new Error('WS连接超时')), 15000);
            this.ws.on('open', () => { clearTimeout(timer); resolve(); });
            this.ws.on('error', (e) => { clearTimeout(timer); reject(e); });
        });
        this.ws.on('message', (d) => {
            try { const m = JSON.parse(d.toString()); console.log('📨', JSON.stringify(m)); } catch { }
        });
    }

    async closeWS() {
        try { this.ws?.close?.(); } catch { }
    }

    async queryRoles() {
        const res = await this.send(3, {});
        const list = (res && res.roleList) ? res.roleList : [];
        this.characters = list;
        if (!Array.isArray(list)) throw new Error('角色列表返回异常');
    }

    async createRole() {
        await this.send(5, {
            name: CONFIG.testCharacter.name,
            sex: CONFIG.testCharacter.sex,
            roleType: CONFIG.testCharacter.roleType,
            headIndex: CONFIG.testCharacter.headIndex,
        });
    }

    async selectFirstRole() {
        const id = this.characters[0]?.id;
        if (!id) throw new Error('无角色可选');
        const res = await this.sendAndExpect(4, { characterId: id }, 0);
        if (res.code === 0) this.selectedCharacterId = id;
    }

    async send(actId, payload = {}) {
        return new Promise((resolve) => {
            const handler = (raw) => {
                try {
                    const msg = JSON.parse(raw.toString());
                    if (msg && msg.act_id === actId) {
                        this.ws.off('message', handler);
                        resolve(msg);
                    }
                } catch { }
            };
            this.ws.on('message', handler);
            this.ws.send(JSON.stringify({ c: actId, d: Object.keys(payload).length ? payload : null }));
            setTimeout(() => { this.ws.off('message', handler); resolve({ act_id: actId, code: -1, msg: 'timeout' }); }, 15000);
        });
    }

    async sendAndExpect(actId, payload = {}, expectCode = 0, timeoutMs = 10000) {
        const res = await this.send(actId, payload);
        if (res.code !== expectCode) throw new Error(res.msg || `act ${actId} 失败`);
        return res;
    }

    httpReq(path, method, body) {
        const options = { hostname: CONFIG.server.host, port: CONFIG.server.port, path, method, headers: { 'Content-Type': 'application/json' } };
        return new Promise((resolve, reject) => {
            const req = http.request(options, (res) => {
                let data = '';
                res.on('data', (c) => (data += c));
                res.on('end', () => {
                    try { resolve(JSON.parse(data || '{}')); } catch { reject(new Error('响应解析失败')); }
                });
            });
            req.on('error', reject);
            if (body) req.write(JSON.stringify(body));
            req.end();
        });
    }

    async waitServer(maxRetry = 20) {
        for (let i = 0; i < maxRetry; i++) {
            try {
                // 探活：发送一个必失败但可连通的请求
                await this.httpReq('/user-service/user/login', 'POST', { mobile: 'x', pwd: 'x' });
                return; // 能走到resolve说明端口可连接
            } catch (e) {
                const msg = String(e?.message || '');
                if (msg.includes('ECONNREFUSED')) {
                    console.log(`⏳ 等待服务启动(${i + 1}/${maxRetry})... http://${CONFIG.server.host}:${CONFIG.server.port}`);
                    await this.delay(1500);
                    continue;
                }
                // 其它错误（业务错误）视为服务已就绪
                return;
            }
        }
        throw new Error(`无法连接到服务 ${CONFIG.server.host}:${CONFIG.server.port}`);
    }

    delay(ms) { return new Promise((r) => setTimeout(r, ms)); }
}

(async () => {
    try {
        const t = new SkillFlowTester();
        await t.run();
    } catch (e) {
        console.error('❌ 测试失败:', e?.message || e);
        process.exit(1);
    }
})();


